package com.ciaojian.core.interceptor;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.ciaojian.core.exception.EntityNotFoundException;
import com.ciaojian.core.util.LogUtil;
import com.ciaojian.core.util.Result;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * @author Atlakyn
 */
@RestControllerAdvice
public class ExceptionInterceptor {
    /**
     * 程序异常
     *
     * @param ex 全部异常
     * @return 打印异常信息到日志并且返回客户端友好提示
     */
    @ExceptionHandler(Exception.class)
    public Result exception(Exception ex) throws JsonProcessingException {
        ex.printStackTrace();
        LogUtil.writeErrorLog(ex, ex.getMessage());
        return Result.error("服务器异常");
    }


    /**
     * 参数异常
     *
     * @param ex 异常对象
     * @return 封装信息
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Result illegalArgumentEx(Exception ex) {
        ex.printStackTrace();
        return Result.error(ex.getMessage());
    }

    /**
     * 方法参数未通过校验
     *
     * @param ex springboot 的参数校验接口 {@link org.springframework.validation.annotation}
     * @return 封装信息
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public Result methodArgumentNotValidException(MethodArgumentNotValidException ex) {
        ex.printStackTrace();
        List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.toList());
        return Result.result(400, collect.toString());
    }

    /**
     * 方法参数未通过校验
     *
     * @param ex javax 的参数校验接口 {@link javax.validation}
     * @return 封装信息
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public Result methodArgumentNotValidException(ConstraintViolationException ex) {
        ex.printStackTrace();
        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
        StringBuilder builder = new StringBuilder();
        for (ConstraintViolation violation : violations) {
            builder.append(violation.getMessage());
            break;
        }
        return Result.result(400, builder.toString());
    }

    /**
     * 绑定参数校验不通过
     *
     * @param ex 绑定参数异常
     * @return 通知前端哪些参数不通过
     */
    @ExceptionHandler(BindException.class)
    public Result bindException(BindException ex) {
        List<FieldError> fieldErrorList = ex.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrorList.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.toList());
        return Result.error(collect.toString());
    }

    /**
     * 拦截请求方式异常
     *
     * @param e 参数异常
     * @return 以R类的格式返回信息
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public Result errorHandler(HttpRequestMethodNotSupportedException e) {
        StringBuilder methods = new StringBuilder();
        //支持的请求方式
        String[] supportedMethods = e.getSupportedMethods();
        assert supportedMethods != null;
        for (String method : supportedMethods) {
            methods.append(method);
        }
        return Result.error(String.format("不支持的请求方式:%s!请尝试使用:%s", e.getMethod(), methods.toString()));
    }

    /**
     * mybatis plus 异常
     */
    @ExceptionHandler({MybatisPlusException.class})
    public void mybatisPlusException(MybatisPlusException e) {
        System.err.println("查看是否建立了对应的 Mapper");
        e.printStackTrace();
    }

    @ExceptionHandler(MyBatisSystemException.class)
    public Result myBatisSystemException(MyBatisSystemException e) {
        e.printStackTrace();
        return Result.error(e.getMessage());
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        return Result.error(String.format("方法参数%s类型不匹配\n需要 '%s',实际的值:'%s'",
                e.getName(),
                e.getRequiredType(),
                e.getValue()));
    }


    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result missingServletRequestParameterException(MissingServletRequestParameterException e) {
        return Result.error("缺少必须的参数:{}", e.getMessage());
    }

    /**
     * SQL 语法异常
     *
     * @param e 语法异常类
     * @return 封装结果
     */
    @ExceptionHandler(BadSqlGrammarException.class)
    public Result badSqlGrammarException(BadSqlGrammarException e) {
        e.printStackTrace();
        System.err.println("==================SQL语句========================");
        System.err.println(e.getMessage());
        System.err.println("================================================");
        LogUtil.writeDbErrorLog("SQL 语法异常,异常信息:{}", e.getSQLException().getMessage());
        return Result.error("系统异常");
    }

    @ExceptionHandler(EntityNotFoundException.class)
    public Result entityNotFoundException(EntityNotFoundException e) {
        e.printStackTrace();
        return Result.error(e.getMessage());
    }
}
